//**************************************************
// Description: arbiter ARP channel and UDP channel  
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module tx_frame_aribiter #(
    parameter   IN_DATA_WIDTH           = 128 ,
    parameter   IN_MOD_WIDTH            = 4
        ) (
    input                               clk ,
    input                               rst_n ,
    // pre-module interface
    // ARP channel
    output                              ARP_frame_rdy , 
    input       [IN_DATA_WIDTH-1:0]     ARP_frame_data ,
    input       [IN_MOD_WIDTH-1:0]      ARP_frame_mod ,
    input                               ARP_frame_sav ,
    input                               ARP_frame_val ,
    input                               ARP_frame_sop ,
    input                               ARP_frame_eop ,
    // UDP channel
    output                              UDP_frame_rdy , 
    input       [IN_DATA_WIDTH-1:0]     UDP_frame_data ,
    input       [IN_MOD_WIDTH-1:0]      UDP_frame_mod ,
    input                               UDP_frame_sav ,
    input                               UDP_frame_val ,
    input                               UDP_frame_sop ,
    input                               UDP_frame_eop ,

    // post-module interface
    input                               tx_bus_convert_rdy ,
    output      [IN_DATA_WIDTH-1:0]     tx_bus_convert_data ,
    output      [IN_MOD_WIDTH-1:0]      tx_bus_convert_mod ,
    output                              tx_bus_convert_sav ,
    output                              tx_bus_convert_val ,
    output                              tx_bus_convert_sop ,
    output                              tx_bus_convert_eop
) ;
    //----------------------------------------------
    // bus convert signal declare
    //----------------------------------------------
    // post-module register
    reg     [IN_DATA_WIDTH-1:0]         tx_bus_convert_data_r ;
    reg     [IN_MOD_WIDTH-1:0]          tx_bus_convert_mod_r ;
    reg                                 tx_bus_convert_sav_r ;
    reg                                 tx_bus_convert_val_r ;
    reg                                 tx_bus_convert_sop_r ;
    reg                                 tx_bus_convert_eop_r ;

//**************************************************
// FSM
//**************************************************
    //----------------------------------------------
    // FSM define
    //----------------------------------------------
    localparam      ARBITER_IDLE        = 3'd0 ;
    localparam      ARBITER_ARP_WAIT    = 3'd1 ;
    localparam      ARBITER_ARP_SEND    = 3'd2 ;
    localparam      ARBITER_UDP_WAIT    = 3'd3 ;
    localparam      ARBITER_UDP_SEND    = 3'd4 ;
    localparam      ARBITER_END         = 3'd5 ;

    reg     [2:0]       arbiter_state_c ;
    reg     [2:0]       arbiter_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            arbiter_state_c <= ARBITER_IDLE ;
        else 
            arbiter_state_c <= arbiter_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case(arbiter_state_c) 
            ARBITER_IDLE: begin
                if(tx_bus_convert_rdy) begin
                    if(UDP_frame_sav)
                        arbiter_state_n = ARBITER_UDP_WAIT ;
                    else if(ARP_frame_sav)
                        arbiter_state_n = ARBITER_ARP_WAIT ;
                    else
                        arbiter_state_n = ARBITER_IDLE ;
                end
                else
                    arbiter_state_n = ARBITER_IDLE ;
            end

            ARBITER_ARP_WAIT: begin
                if( ARP_frame_sop )
                    arbiter_state_n = ARBITER_ARP_SEND ;
                else
                    arbiter_state_n = ARBITER_ARP_WAIT ;
            end

            ARBITER_ARP_SEND: begin
                if( ARP_frame_eop )
                    arbiter_state_n = ARBITER_END ;
                else
                    arbiter_state_n = ARBITER_ARP_SEND ;
            end

            ARBITER_UDP_WAIT: begin
                if( UDP_frame_sop )
                    arbiter_state_n = ARBITER_UDP_SEND ;
                else
                    arbiter_state_n = ARBITER_UDP_WAIT ;
            end

            ARBITER_UDP_SEND: begin
                if( UDP_frame_eop )
                    arbiter_state_n = ARBITER_END ;
                else
                    arbiter_state_n = ARBITER_UDP_SEND ;
            end

            ARBITER_END: begin
                arbiter_state_n = ARBITER_IDLE ;
            end

            default: 
                arbiter_state_n = ARBITER_IDLE ;
        endcase
    end
    // FSM(3)
    //----------------------------------------------
    // arp channel
    //----------------------------------------------
    // "sav" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            tx_bus_convert_sav_r <= 1'b0 ;
        else if( 
                    (tx_bus_convert_rdy) &&
                    (arbiter_state_c == ARBITER_IDLE) &&
                    ((UDP_frame_sav) || (ARP_frame_sav)) 
                )    
            tx_bus_convert_sav_r <= 1'b1 ;
        else if( arbiter_state_c == ARBITER_END )
            tx_bus_convert_sav_r <= 1'b0 ;
        else
            tx_bus_convert_sav_r <= tx_bus_convert_sav_r ;
    end
    // "val" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            tx_bus_convert_val_r <= 1'b0 ;
        else if( 
                    ((arbiter_state_c == ARBITER_ARP_WAIT) && (ARP_frame_sop)) ||
                    ((arbiter_state_c == ARBITER_UDP_WAIT) && (UDP_frame_sop)) ||
                    (arbiter_state_c == ARBITER_ARP_SEND) ||
                    (arbiter_state_c == ARBITER_UDP_SEND)
                )
            tx_bus_convert_val_r <= 1'b1 ;
        else
            tx_bus_convert_val_r <= 1'b0 ;
    end
    // "sop" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            tx_bus_convert_sop_r <= 1'b0 ;
        else if( ((arbiter_state_c == ARBITER_ARP_WAIT) && (ARP_frame_sop)) ||
                    ((arbiter_state_c == ARBITER_UDP_WAIT) && (UDP_frame_sop)) )
            tx_bus_convert_sop_r <= 1'b1 ;
        else
            tx_bus_convert_sop_r <= 1'b0 ;
    end
    // "eop" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            tx_bus_convert_eop_r <= 1'b0 ;
        else if( ((arbiter_state_c == ARBITER_ARP_SEND) && (ARP_frame_eop)) ||
                    ((arbiter_state_c == ARBITER_UDP_SEND) && (UDP_frame_eop)) )
            tx_bus_convert_eop_r <= 1'b1 ;
        else
            tx_bus_convert_eop_r <= 1'b0 ;
    end
    // "data" and "mod"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            tx_bus_convert_data_r   <= {(IN_DATA_WIDTH){1'b0}} ;
            tx_bus_convert_mod_r    <= {(IN_MOD_WIDTH){1'b0}} ;
        end
        else if( ((arbiter_state_c == ARBITER_UDP_WAIT) && (UDP_frame_sop)) ||
                    (arbiter_state_c == ARBITER_UDP_SEND) ) begin
            tx_bus_convert_data_r   <= UDP_frame_data ;
            tx_bus_convert_mod_r    <= UDP_frame_mod ;     
        end
        else if( ((arbiter_state_c == ARBITER_ARP_WAIT) && (ARP_frame_sop)) ||
                    (arbiter_state_c == ARBITER_ARP_SEND) ) begin
            tx_bus_convert_data_r   <= ARP_frame_data ;
            tx_bus_convert_mod_r    <= ARP_frame_mod ;
        end
        else begin
            tx_bus_convert_data_r   <= {(IN_DATA_WIDTH){1'b0}} ;
            tx_bus_convert_mod_r    <= {(IN_MOD_WIDTH){1'b0}} ;
        end
    end


    // output assign
    // rdy
    assign ARP_frame_rdy = ((arbiter_state_c == ARBITER_IDLE) || (arbiter_state_c == ARBITER_ARP_WAIT)) ? 1'b1:1'b0 ;
    assign UDP_frame_rdy = ((arbiter_state_c == ARBITER_IDLE) || (arbiter_state_c == ARBITER_UDP_WAIT)) ? 1'b1:1'b0 ;
    // post module
    assign tx_bus_convert_data  = tx_bus_convert_data_r ;
    assign tx_bus_convert_mod   = tx_bus_convert_mod_r ;
    assign tx_bus_convert_sav   = tx_bus_convert_sav_r ;
    assign tx_bus_convert_val   = tx_bus_convert_val_r ;
    assign tx_bus_convert_sop   = tx_bus_convert_sop_r ;
    assign tx_bus_convert_eop   = tx_bus_convert_eop_r ;

endmodule
